
public class MyBinaryTree<S extends Comparable<? super S>> {
	
	public S elem;
	private MyBinaryTree<S> left;
	private MyBinaryTree<S> right;
	private final int LEFT=-1;
	private final int RIGHT=1;
	private final int NOTAVL=-1;
	
	public<T extends Comparable<? super T>> MyBinaryTree(MyBinaryTree<T> bt,Function<T,S> f){
		treeInject(bt,f);
	}
	
	private <T extends Comparable<? super T>> MyBinaryTree<S> treeInject(MyBinaryTree<T> bt,Function<T,S> f){
		if(bt==null){
			return null;
		}
		
		MyBinaryTree<S> aux=new MyBinaryTree<S>(f.evaluate(bt.elem));
		aux.left=treeInject(bt.left,f);
		aux.right=treeInject(bt.right,f);
		return aux;
		
	}
	
	public MyBinaryTree(S elem){
		this.elem=elem;
		this.left=null;
		this.right=null;
	}
	
	public MyBinaryTree(S elem, MyBinaryTree<S> left, MyBinaryTree<S> right){
		this.elem=elem;
		this.left=left;
		this.right=right;
	}

	public boolean isBST(){
		if(left!=null){
			if(this.elem.compareTo(left.elem)<=0||!left.isBST(this.elem,LEFT)){
				return false;
			}
		}
		if(right!=null){
			if(this.elem.compareTo(right.elem)>=0||!right.isBST(this.elem,RIGHT)){
				return false;
			}
		}
		
		return true;
	}
	
	private boolean isBST(S elem, int direction){
		switch(direction){
		case LEFT:
			if(this.elem.compareTo(elem)>=0){
				return false;
			}
			break;
		case RIGHT:
			if(this.elem.compareTo(elem)<=0){
				return false;
			}
			break;
		}
			
		if(left!=null){
			if(this.elem.compareTo(left.elem)<=0||!(left.isBST(this.elem,LEFT)&&left.isBST(elem,direction))){
				return false;
			}
		}
		if(right!=null){
			if(this.elem.compareTo(right.elem)>=0||!(right.isBST(this.elem,RIGHT)&&right.isBST(elem,direction))){
				return false;
			}
		}
	
		return true;
	}

	public boolean isAVL(){
		int l=0;
		int r=0;
		
		if(left!=null){
			l=left.isAVL(this.elem,LEFT,0);
			if(this.elem.compareTo(left.elem)<=0||l==NOTAVL){
				return false;
			}
		}
		if(right!=null){
			r=right.isAVL(this.elem,RIGHT,0);
			if(this.elem.compareTo(right.elem)>=0||r==NOTAVL){
				return false;
			}
		}
		
		return Math.abs(l-r)<=1;
	}
	
	private int isAVL(S elem, int direction, int height){
		switch(direction){
		case LEFT:
			if(this.elem.compareTo(elem)>=0){
				return NOTAVL;
			}
			break;
		case RIGHT:
			if(this.elem.compareTo(elem)<=0){
				return NOTAVL;
			}
			break;
		}
		
		int l=0;
		int r=0;
		
		if(left!=null){
			if(this.elem.compareTo(left.elem)<=0){
				return NOTAVL;
			}
			l=left.isAVL(elem,direction,height+1);
			if(l==NOTAVL){
				return NOTAVL;
			}
			l=left.isAVL(this.elem,LEFT,height+1);
			if(l==NOTAVL){
				return NOTAVL;
			}
		}
		if(right!=null){
			if(this.elem.compareTo(right.elem)>=0){
				return NOTAVL;
			}
			r=right.isAVL(elem,direction,height+1);
			if(r==NOTAVL){
				return NOTAVL;
			}
			r=right.isAVL(this.elem,RIGHT,height+1);
			if(r==NOTAVL){
				return NOTAVL;
			}
		}
		if(left==null && right==null){
			return height;
		}
		
		if(Math.abs(r-l)>1){
			return NOTAVL;
		}
		return Math.max(r,l);
	}
	
	private <T extends Comparable<? super T>> boolean mirrorsMe(MyBinaryTree<T> bt){
		boolean myVal=false;
		if((bt.left==null&&bt.right==null&&left==null&&right==null)){
			return true;
		}else if(bt.right!=null&&left!=null){
			myVal=bt.right.mirrorsMe(bt.left);
		}else if(bt.left!=null&&right!=null){
			myVal=myVal&&bt.left.mirrorsMe(bt.right);
		}
		return myVal;
	}
}
